package com.mocchira.music.radio.web;

import java.io.IOException;
import java.io.Writer;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bson.BSONObject;
import org.bson.BasicBSONObject;
import org.bson.types.BasicBSONList;

import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.Mongo;
import com.mongodb.MongoException;

public class MongoMusicResourceManager implements IMusicResourceManager {
    Log log = LogFactory.getLog(Class.class);
    
    private Mongo m = null;
    private com.mongodb.DB db = null;
    private DBCollection colTags = null;
    private DBCollection colArtists = null;
    private DBCollection colArtistsLog = null;
    private DBCollection colHotArtists = null;
    private static final String DB = "fmr";
    private static final String COL_ARTISTS = "artists";
    private static final String COL_ARTISTS_LOG = "artists_log";
    private static final String COL_HOT_ARTISTS = "hot_artists";
    private static final String LOGCOL_ID_SEP = "_";
    private static final String COL_TAGS = "tags";
    private static final int MAX_RETURN = 5;
    
    public MongoMusicResourceManager(String host, int port) throws MongoException, UnknownHostException {
        m = new Mongo(host, port);
        m.slaveOk();
        db = m.getDB(DB);
        colArtists = db.getCollection(COL_ARTISTS);
        colTags = db.getCollection(COL_TAGS);
        colArtistsLog = db.getCollection(COL_ARTISTS_LOG);
        colHotArtists = db.getCollection(COL_HOT_ARTISTS);
    }

    private BSONObject getNTrackList(String tag, int startPos) {
        BasicDBObject query = new BasicDBObject();
        BasicDBObject fields = new BasicDBObject();
        query.put("tags", tag);
        DBCursor cur = colArtists.find(query, fields);
        int cnt = cur.count();
        if ((startPos + MAX_RETURN) > cnt) startPos = 0;
        if (startPos < 0)
            startPos = (new Random()).nextInt(cnt - MAX_RETURN);
        cur.limit(MAX_RETURN);
        cur.skip(startPos);
        cur.snapshot();
        BasicBSONList list = new BasicBSONList();
        while (cur.hasNext()) {
            DBObject artist = cur.next();
            // check
            BasicDBList albums = (BasicDBList)artist.get("albums");
            if (albums == null || albums.size() == 0) continue;
            int i = 0;
            if (albums.size() > 1)
                i = (new Random()).nextInt(albums.size());
            DBObject album = (DBObject)albums.get(i);
            BasicDBList tracks = (BasicDBList)album.get("tracks");
            if (tracks == null || tracks.size() == 0) continue;
            int ii = 0;
            if (tracks.size() > 1)
                ii = (new Random()).nextInt(tracks.size());
            DBObject track = (DBObject)tracks.get(ii);
            BasicBSONObject item = new BasicBSONObject();
            item.put("artist_name", artist.get("_id"));
            item.put("artist_img_url", artist.get("img_large_url"));
            item.put("artist_lastfm_url", artist.get("lastfm_url"));
            item.put("album_name", album.get("name"));
            item.put("album_img_url", album.get("img_large_url"));
            item.put("album_lastfm_url", album.get("lastfm_url"));
            item.put("track_name", track.get("name"));
            item.put("track_lastfm_url", track.get("lastfm_url"));
            list.add(item);
        }
        BasicBSONObject root = new BasicBSONObject();
        root.put("tracks", list);
        return root;
    }
    @Override
    public void writeJSONTrackList(String tag, Writer writer) throws IOException  {
        writeJSONTrackList(tag, writer, 0);
    }

    @Override
    public void writeJSONTrackList(String tag, Writer writer, int startPos) throws IOException {
        BSONObject root = getNTrackList(tag, startPos);
        BasicBSONList tracks = (BasicBSONList)root.get("tracks");
        for (Object obj: tracks) {
            BasicBSONObject track = (BasicBSONObject)obj;
            BasicDBObject query = new BasicDBObject();
            BasicDBObject regex = new BasicDBObject();
            BasicDBObject fields = new BasicDBObject();
            String prefix = "^" + StringUtil.escapeRegexStr(track.getString("artist_name")) + LOGCOL_ID_SEP +
                                  StringUtil.escapeRegexStr(track.getString("album_name")) + LOGCOL_ID_SEP +
                                  StringUtil.escapeRegexStr(track.getString("track_name"));
            regex.put("$regex", prefix);
            query.put("_id", regex);
            DBCursor cur = colArtistsLog.find(query, fields);
            cur.limit(1);
            BasicDBObject orderBy = new BasicDBObject();
            orderBy.put("logged_date", -1);
            cur.sort(orderBy);
            if (cur.hasNext()) {
                DBObject rec = cur.next();
                track.put("logged_url", rec.get("logged_url"));
                track.put("player_html", rec.get("player_html"));
            }
        }
        writer.write(root.toString());
        writer.flush();
    }

    @Override
    public void writeTopNTagList(int topn, Writer writer) throws IOException {
        DBCursor cur = colTags.find();
        cur.limit(topn);
        BasicDBObject orderBy = new BasicDBObject();
        orderBy.put("value.count", -1);
        cur.sort(orderBy);
        BasicBSONList list = new BasicBSONList();
        while (cur.hasNext()) {
            DBObject obj = cur.next();
            list.add(obj.get("_id"));
        }
        BasicBSONObject root = new BasicBSONObject();
        root.put("tags", list);
        writer.write(root.toString());
        writer.flush();
    }

    @Override
    public void close() {
        m.close();
    }

    @Override
    public void writeJSONHotArtistList(Writer writer) throws Exception {
        List<Object> list = getHotArtistList(100);
        BasicBSONObject root = new BasicBSONObject();
        root.put("artists", list);
        writer.write(root.toString());
        writer.flush();
    }

    @Override
    public List<Object> writeJSONArtistTrackList(String artist, Writer writer)
            throws Exception {
        BasicDBObject query = new BasicDBObject("_id", artist);
        BasicDBObject fields = new BasicDBObject();
        DBObject artistObj = colArtists.findOne(query, fields);
        BasicBSONList list = new BasicBSONList();
        BasicDBList albums = (BasicDBList)artistObj.get("albums");
        if (albums != null && albums.size() > 0) {
            for (Object album: albums) {
                DBObject albumObj = (DBObject)album;
                BasicDBList tracks = (BasicDBList)albumObj.get("tracks");
                if (tracks == null || tracks.size() == 0) continue;
                for (Object track: tracks) {
                    DBObject trackObj = (DBObject)track;
                    BasicBSONObject item = new BasicBSONObject();
                    try {
                        item.put("artist_name", artistObj.get("_id"));
                        item.put("artist_img_url", artistObj.get("img_large_url"));
                        item.put("artist_lastfm_url", artistObj.get("lastfm_url"));
                        item.put("album_name", albumObj.get("name"));
                        item.put("album_img_url", albumObj.get("img_large_url"));
                        item.put("album_lastfm_url", albumObj.get("lastfm_url"));
                        item.put("track_name", trackObj.get("name"));
                        item.put("track_lastfm_url", trackObj.get("lastfm_url"));
                        query = new BasicDBObject();
                        BasicDBObject regex = new BasicDBObject();
                        fields = new BasicDBObject();
                        String prefix = "^" + StringUtil.escapeRegexStr(item.getString("artist_name")) + LOGCOL_ID_SEP +
                                              StringUtil.escapeRegexStr(item.getString("album_name")) + LOGCOL_ID_SEP +
                                              StringUtil.escapeRegexStr(item.getString("track_name"));
                        regex.put("$regex", prefix);
                        query.put("_id", regex);
                        DBCursor cur = colArtistsLog.find(query, fields);
                        cur.limit(1);
                        BasicDBObject orderBy = new BasicDBObject();
                        orderBy.put("logged_date", -1);
                        cur.sort(orderBy);
                        if (cur.hasNext()) {
                            DBObject rec = cur.next();
                            item.put("logged_url", rec.get("logged_url"));
                            item.put("player_html", rec.get("player_html"));
                            list.add(item);
                        }
                    } catch (Exception ex) {};
                }
            }
        }
        Collections.shuffle(list);
        BasicBSONObject root = new BasicBSONObject();
        root.put("tracks", list);
        writer.write(root.toString());
        writer.flush();
        return (BasicDBList)artistObj.get("tags");
    }

    @Override
    public List<Object> getHotArtistList() throws Exception {
        return getHotArtistList(-1);
    }
    
    @Override
    public List<Object> getHotArtistList(int num) throws Exception {
        BasicDBObject query = new BasicDBObject();
        BasicDBObject fields = new BasicDBObject("_id", 1);
        BasicDBObject orderBy = new BasicDBObject("value.count", -1);
        DBCursor cur = colHotArtists.find(query, fields);
        if (num > 0)
            cur.limit(num);
        cur.sort(orderBy);
        BasicBSONList list = new BasicBSONList();
        while (cur.hasNext()) {
            DBObject obj = cur.next();
            list.add(obj.get("_id"));
        }
        return list;
    }

    @Override
    public List<Object> getArtistListByTag(String tag) throws Exception {
        BasicDBObject query = new BasicDBObject("tags", tag);
        BasicDBObject fields = new BasicDBObject("_id", 1);
        DBCursor cur = colArtists.find(query, fields);
        BasicBSONList list = new BasicBSONList();
        while (cur.hasNext()) {
            DBObject obj = cur.next();
            list.add(obj.get("_id"));
        }
        return list;
    }

    @Override
    public Map<String, List<Object>> getLatestTracks() throws Exception {
        Map<String, List<Object>> tracksPerTags = new HashMap<String, List<Object>>();
        Long from = System.currentTimeMillis() - (3600 * 24 * 1000); // from 1day ago 1299480199000
        //Long from = 1298708530000L;
        BasicDBObject gte = new BasicDBObject("$gte", from);
        BasicDBObject query = new BasicDBObject("logged_date", gte);
        BasicDBObject fields = new BasicDBObject();
        DBCursor cur = colArtistsLog.find(query, fields);
        while (cur.hasNext()) {
            DBObject log = cur.next();
            String[] ids = ((String)log.get("_id")).split(LOGCOL_ID_SEP);
            if (ids.length < 3) continue;
            String artistKey = ids[0];
            String albumKey  = ids[1];
            String trackKey  = ids[2];
            boolean find = false;
            BasicBSONObject item = new BasicBSONObject();
            query = new BasicDBObject("_id", artistKey);
            fields = new BasicDBObject();
            DBObject artistObj = colArtists.findOne(query, fields);
            if (artistObj == null) continue;
            DBObject albumObj = null;
            DBObject trackObj = null;
            BasicDBList albums = (BasicDBList)artistObj.get("albums");
            if (albums == null) continue;
            for (Object album: albums) {
                albumObj = (DBObject)album;
                String name = (String)albumObj.get("name");
                if (name == null || !name.equals(albumKey)) continue;
                BasicDBList tracks = (BasicDBList)albumObj.get("tracks");
                if (tracks == null) continue;
                for (Object track: tracks) {
                    trackObj = (DBObject)track;
                    name = (String)trackObj.get("name");
                    if (name != null && name.equals(trackKey)) {
                        find = true;
                        break;
                    }
                }
                if (find) break;
            }
            if (!find) continue;
            try {
                item.put("artist_name", artistObj.get("_id"));
                item.put("artist_img_url", artistObj.get("img_large_url"));
                item.put("artist_lastfm_url", artistObj.get("lastfm_url"));
                item.put("album_name", albumObj.get("name"));
                item.put("album_img_url", albumObj.get("img_large_url"));
                item.put("album_lastfm_url", albumObj.get("lastfm_url"));
                item.put("track_name", trackObj.get("name"));
                item.put("track_lastfm_url", trackObj.get("lastfm_url"));
                item.put("logged_url", log.get("logged_url"));
                item.put("player_html", log.get("player_html"));
                // append list
                BasicDBList tags = (BasicDBList)artistObj.get("tags");
                if (tags == null || tags.size() == 0) continue;
                List<Object> targetList = tracksPerTags.get((String)tags.get(0));
                if (targetList == null) {
                    targetList = new BasicBSONList();
                    tracksPerTags.put((String)tags.get(0), targetList);
                }
                targetList.add(item);
            } catch (Exception ex) {};
        }
        return tracksPerTags;
    }

}
